package site.https;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author haocongshun
 * @date 2023/03/31 9:42:31
 */
public class CnIP2IpRegion {


    public static void main(String[] args) {
        String[] lines = ResourceUtil.readUtf8Str("cn-ip.txt").split("\n");
        List<Ip> ips = Arrays.stream(lines)
                .map(CnIP2IpRegion::convert)
                .sorted()
                .collect(Collectors.toList());

        Iterator<Ip> iterator = ips.iterator();
        List<Ip> fillIps = new LinkedList<>();
        Ip last = null;
        while (iterator.hasNext()) {
            Ip next = iterator.next();
            if (last == null) {
                last = next;
                continue;
            }
            Ip ip = next.dValue(last);
            if (ip != null) {
                fillIps.add(ip.setFlag("unknown"));
            }
            last = next;
        }
        ips.addAll(fillIps);
        ips = ips.stream().sorted().collect(Collectors.toList());
        FileUtil.writeUtf8Lines(ips.stream().map(Ip::toIpRegionString).collect(Collectors.toList()), "D:\\workspace\\personal\\ip2region\\maker\\java\\src\\main\\resources\\cn-ip.txt");
        System.out.println();
    }


    public static Ip convert(String cidr) {
        String[] parts = cidr.split("/");
        String ip = parts[0];
        int prefixLength = Integer.parseInt(parts[1]);
        try {
            InetAddress inetAddress = InetAddress.getByName(ip);
            byte[] address = inetAddress.getAddress();
            int mask = 0xffffffff << (32 - prefixLength);
            byte[] netmask = new byte[]{
                    (byte) (mask >>> 24),
                    (byte) (mask >> 16 & 0xff),
                    (byte) (mask >> 8 & 0xff),
                    (byte) (mask & 0xff)
            };
            byte[] network = new byte[4];
            for (int i = 0; i < 4; i++) {
                network[i] = (byte) (address[i] & netmask[i]);
            }
            byte[] broadcast = new byte[4];
            for (int i = 0; i < 4; i++) {
                broadcast[i] = (byte) (address[i] | ~netmask[i]);
            }
            InetAddress startAddress = InetAddress.getByAddress(network);
            InetAddress endAddress = InetAddress.getByAddress(broadcast);
            return new Ip(cidr, startAddress.getHostAddress(), endAddress.getHostAddress());
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }

    @Getter
    public static class Ip implements Comparable<Ip> {

        private final String source;

        private final String start;
        private final String end;

        private final long startNumber;

        private final long endNumber;

        @Setter
        @Accessors(chain = true)
        private String flag = "中国";

        public Ip(String source, String start, String end) {
            this.source = source;
            this.start = start;
            this.end = end;
            this.startNumber = toNumber(start);
            this.endNumber = toNumber(end);
        }

        public Ip(String source, Long startNumber, Long endNumber) {
            this.source = source;
            this.startNumber = startNumber;
            this.endNumber = endNumber;
            this.start = toIp(startNumber);
            this.end = toIp(endNumber);
        }

        public Ip dValue(Ip ip) {
            if (this.endNumber + 1 < ip.startNumber) {
                return new Ip("unknown", this.endNumber + 1, ip.startNumber - 1);
            } else if (this.startNumber > ip.endNumber + 1) {
                return new Ip("unknown", ip.endNumber + 1, this.startNumber - 1);
            } else {
                return null;
            }
        }

        private long toNumber(String ip) {
            String[] split = ip.split("\\.");
            return Integer.parseInt(split[0]) * 256 * 256 * 256L
                    + Integer.parseInt(split[1]) * 256 * 256L
                    + Integer.parseInt(split[2]) * 256L
                    + Long.parseLong(split[3]);
        }

        private String toIp(long num) {
            //等价上面
            List<String> list = new LinkedList<>();
            StringBuilder ip = new StringBuilder();
            for (int i = 3; i >= 0; i--) {
                ip.append(num & 0xff);
                if (i != 0) {
                    list.add(0, ip.toString());
                    ip.delete(0, ip.length());
                }
                num = num >> 8;
            }
            if (ip.length() > 0) {
                list.add(0, ip.toString());
            }
            return String.join(".", list);
        }

        public String toIpRegionString() {
            String pattern = "%s|%s|%s";
            return String.format(pattern, start, end, flag);
        }

        @Override
        public int compareTo(Ip o) {
            return Long.compare(this.startNumber, o.startNumber);
        }
    }
}
